package com.gnerv.sylvanas.authentication.security.autoconfigure;

import com.gnerv.sylvanas.authentication.security.config.filter.VerificationCodeFilter;
import com.gnerv.sylvanas.authentication.security.config.handler.CustomAccessDeniedHandler;
import com.gnerv.sylvanas.authentication.security.config.handler.CustomAuthenticationEntryPoint;
import com.gnerv.sylvanas.authentication.security.config.handler.CustomAuthenticationFailureHandler;
import com.gnerv.sylvanas.authentication.security.config.handler.CustomAuthenticationSuccessHandler;
import com.gnerv.sylvanas.authentication.security.config.handler.CustomLogoutSuccessHandler;
import com.gnerv.sylvanas.authentication.security.service.UriAuthorityService;
import com.gnerv.sylvanas.authentication.security.service.VerificationCodeService;
import com.gnerv.sylvanas.authentication.security.tool.SecurityTools;
import com.gnerv.sylvanas.framework.core.BaseAccountPassword;
import com.gnerv.sylvanas.framework.core.BaseCurrentAccount;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.util.Collection;
import java.util.List;


/**
 * @author Gnerv LiGen
 */
@Slf4j
@AutoConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(SylvanasSecurityConfigProperties.class)
public class SylvanasSecurityAutoConfiguration {

    @Resource
    private SylvanasSecurityConfigProperties sylvanasSecurityConfigProperties;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return new CustomAccessDeniedHandler();
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return new CustomAuthenticationEntryPoint();
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new CustomAuthenticationSuccessHandler();
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return new CustomAuthenticationFailureHandler();
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        return new CustomLogoutSuccessHandler();
    }

    @Bean
    public VerificationCodeService verificationCodeService() {
        return (servletRequest, servletResponse) -> true;
    }

    @Bean
    @ConditionalOnBean(value = {PasswordEncoder.class})
    public BaseAccountPassword baseAccountPassword(PasswordEncoder passwordEncoder) {
        return new BaseAccountPassword(){
            @Override
            public String encrypt(String source) {
                return passwordEncoder.encode(source);
            }
        };
    }

    @Bean
    @ConditionalOnClass(value = SecurityTools.class)
    public BaseCurrentAccount baseCurrentAccount() {
        return new BaseCurrentAccount() {
            @Override
            public String ukId() {
                return SecurityTools.userDetails().getUkId();
            }

            @Override
            public String username() {
                return SecurityTools.username();
            }
        };
    }

    @Bean
    @ConditionalOnMissingBean
    public UriAuthorityService uriAuthorityService() {
        return new UriAuthorityService() {

            @Override
            public String selectAuthority(String uri, String type) {
                return "";
            }

            @Override
            public boolean verify(Collection<? extends GrantedAuthority> authorities, String authority) {
                return true;
            }
        };
    }

    @Bean
    @ConditionalOnMissingBean
    public VerificationCodeFilter verificationCodeFilter() {
        return new VerificationCodeFilter(sylvanasSecurityConfigProperties.getLoginPageUrl());
    }

    @Bean
    @ConditionalOnMissingBean(value = {UserDetailsService.class})
    public UserDetailsService userDetailsService() {
        return username -> new User(username, passwordEncoder().encode(username), List.of());
    }

    @Bean
    @ConditionalOnMissingBean
    public DaoAuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService,
                                                               PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder);
        return provider;
    }

}
